/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
 * Description: watchdog driver
 * Author: huawei
 * Create: 2020-9-27
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/param.h>
#include <linux/jiffies.h>
#include <linux/version.h>
#include <linux/io.h>
#include "drv_log.h"
#include "watchdog.h"

#define WRITE_WG_REG(offset, value) (*(volatile unsigned long*)(g_watchdogBase + (offset)) = (value))

static struct timer_list g_watchdogTimer;
static void __iomem *g_watchdogBase = NULL;

STATIC void watchdog_hw_init(void)
{
    WRITE_WG_REG(WATCHDOG_WCS_OFFSET, DISABLE_WATCHDOG_VALUE);
    WRITE_WG_REG(WATCHDOG_WOR_OFFSET, WATCHDOG_MS_1 * WATCHDOG_THRESHOLD * MILLISECONDS_ONE_SECOND);
    WRITE_WG_REG(WATCHDOG_WCS_OFFSET, EANBLE_WATCHDOG_VALUE);
}

STATIC void watchdog_feed(void)
{
    WRITE_WG_REG(WATCHDOG_WOR_OFFSET, WATCHDOG_MS_1 * WATCHDOG_THRESHOLD * MILLISECONDS_ONE_SECOND);
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
STATIC void watchdog_handler(unsigned long time)
#else
STATIC void watchdog_handler(struct timer_list *t)
#endif
{
    watchdog_feed();
    /* Reset kernl timer after feed dog */
    mod_timer(&g_watchdogTimer, jiffies + msecs_to_jiffies(FEED_FREQUENCY * MILLISECONDS_ONE_SECOND));
}

STATIC void watchdog_timer_setup(unsigned char feed_time)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
    init_timer(&g_watchdogTimer);
    g_watchdogTimer.function = watchdog_handler;
#else
    timer_setup(&g_watchdogTimer, watchdog_handler, 0);
#endif
    g_watchdogTimer.expires = jiffies + HZ * feed_time;
    add_timer(&g_watchdogTimer);
}

STATIC int __init watchdog_init(void)
{
    g_watchdogBase = ioremap(WDOG0_REG_BASE, SIZE_OF_40B);
    if (g_watchdogBase == NULL) {
        dev_watchdog_err("remap watchdog base failed.\n");
        return -ENAVAIL;
    }
    watchdog_hw_init();
    watchdog_timer_setup(FEED_FREQUENCY);

    dev_watchdog_info("watchdog_init ok.\n");
    return 0;
}

STATIC void __exit watchdog_exit(void)
{
    del_timer_sync(&g_watchdogTimer);
    WRITE_WG_REG(WATCHDOG_WCS_OFFSET, DISABLE_WATCHDOG_VALUE);
    iounmap(g_watchdogBase);

    dev_watchdog_info("watchdog timer exit.\n");
}

module_init(watchdog_init);
module_exit(watchdog_exit);
MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("dev feed watchdog driver");